#include "SPILcd.hpp"
#include "main.h"
#include "stm32f4xx_hal.h"
#include "stdlib.h"
/* RTOS */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
/* 调试 */
#include "usart1_printf.h"

SPILcd::SPILcd(unsigned char index) : lcd_index(index) {
    if (index == 0) {
        hspi = &hspi3;
        cs_port = LCD_CS_GPIO_Port;
        cs_pin = LCD_CS_Pin;
        dc_port = LCD_DC_GPIO_Port;
        dc_pin = LCD_DC_Pin;
        bl_port = LCD_BL_GPIO_Port;
        bl_pin = LCD_BL_Pin;
    }
}

SPILcd* SPILcd::getInstance(unsigned char index) {
    static SPILcd instance0(0);
    static SPILcd instance1(1);
    static SPILcd* instances[2] = { &instance0, &instance1 };

    if (index >= 2) return nullptr;

    return instances[index];
}

bool SPILcd::init() {
    if (this->initialized) return true;
    
    // 为了传输效率，SPI通信采用寄存器操作，不用HAL库自带的收发函数，所以需要单独配置下面2个   
    __HAL_SPI_ENABLE(this->hspi);       // 开启 SPI 外设
    SPI_1LINE_TX(this->hspi);           // 设置为单线发送模式（MOSI）

    vTaskDelay(pdMS_TO_TICKS(10)); // 上电后等待稳定（10ms）

    this->beginWrite(); // 拉低 CS，准备通信

    this->writeCommand(0x36); this->writeData(0x00); // 显存访问控制：从左到右，从上到下，RGB格式

    this->writeCommand(0x3A); this->writeData(0x05); // 像素格式：16位色

    this->writeCommand(0xB2);
    this->writeData(0x0C); this->writeData(0x0C); 
    this->writeData(0x00); this->writeData(0x33); 
    this->writeData(0x33);

    this->writeCommand(0xB7); this->writeData(0x35);
    this->writeCommand(0xBB); this->writeData(0x19);
    this->writeCommand(0xC0); this->writeData(0x2C);
    this->writeCommand(0xC2); this->writeData(0x01);
    this->writeCommand(0xC3); this->writeData(0x12);
    this->writeCommand(0xC4); this->writeData(0x20);
    this->writeCommand(0xC6); this->writeData(0x0F);
    this->writeCommand(0xD0); this->writeData(0xA4); this->writeData(0xA1);

    this->writeCommand(0xE0);
    this->writeData(0xD0); this->writeData(0x04); this->writeData(0x0D); this->writeData(0x11);
    this->writeData(0x13); this->writeData(0x2B); this->writeData(0x3F); this->writeData(0x54);
    this->writeData(0x4C); this->writeData(0x18); this->writeData(0x0D); this->writeData(0x0B);
    this->writeData(0x1F); this->writeData(0x23);

    this->writeCommand(0xE1);
    this->writeData(0xD0); this->writeData(0x04); this->writeData(0x0C); this->writeData(0x11);
    this->writeData(0x13); this->writeData(0x2C); this->writeData(0x3F); this->writeData(0x44);
    this->writeData(0x51); this->writeData(0x2F); this->writeData(0x1F); this->writeData(0x1F);
    this->writeData(0x20); this->writeData(0x23);

    this->writeCommand(0x21); // 打开反显

    this->writeCommand(0x11); // 退出休眠
    vTaskDelay(pdMS_TO_TICKS(120)); // 等待120ms

    this->writeCommand(0x29); // 打开显示

    this->endWrite(); // 拉高 CS，结束通信

    this->setDirection(Direction_V_Flip);  	      // 设置显示方向
	this->setBackColor(LCD_BLACK);                // 设置背景色
    this->setColor(LCD_WHITE);                    // 设置画笔色  
    this->setNumMode(Fill_Space);
    this->clear();

    this->setAsciiFont(&ASCII_Font24);       // 设置默认字体
    this->enableBacklight(); // 开启背光

    this->initialized = true;
    return true;
}

void SPILcd::beginWrite() {
    HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_RESET);
}

void SPILcd::endWrite() {
    HAL_GPIO_WritePin(cs_port, cs_pin, GPIO_PIN_SET);
}

void SPILcd::dcCommand() {
    HAL_GPIO_WritePin(dc_port, dc_pin, GPIO_PIN_RESET);
}

void SPILcd::dcData() {
    HAL_GPIO_WritePin(dc_port, dc_pin, GPIO_PIN_SET);
}

void SPILcd::writeCommand(uint8_t cmd) {
    /* 寄存器版本 */
    // 等待 SPI 不忙
    while ((this->hspi->Instance->SR & 0x0080) != RESET);
    this->dcCommand();
    this->hspi->Instance->DR = cmd;
    while ((this->hspi->Instance->SR & 0x0002) == 0);
    while ((this->hspi->Instance->SR & 0x0080) != RESET);
    this->dcData();


    // /* 库函数版本 */
    // dcCommand();
    // HAL_SPI_Transmit(hspi, &cmd, 1, HAL_MAX_DELAY);
    // dcData();
}

void SPILcd::writeData(uint8_t data) {
    /* 寄存器版本 */
    this->hspi->Instance->DR = data;									// 发送数据
	while( (this->hspi->Instance->SR & 0x0002) == 0);		                // 等待发送缓冲区清空

    // /* 库函数版本 */
    // HAL_SPI_Transmit(hspi, &data, 1, HAL_MAX_DELAY);
}

void SPILcd::writeData16(uint16_t data) {
    /* 寄存器版本 */
    this->hspi->Instance->DR = data>>8;						// 发送数据，高8位
	while( (this->hspi->Instance->SR & 0x0002) == 0);		// 等待发送缓冲区清空
	this->hspi->Instance->DR = data;						// 发送数据，低8位
	while( (this->hspi->Instance->SR & 0x0002) == 0);		// 等待发送缓冲区清空

    // /* 库函数版本 */
    // uint8_t buf[2] = { static_cast<uint8_t>(data >> 8), static_cast<uint8_t>(data & 0xFF) };
    // HAL_SPI_Transmit(hspi, buf, 2, HAL_MAX_DELAY);
}

void SPILcd::writeBuff(uint16_t *data, uint16_t length) {
    /* 寄存器版本 */
    // 切换为 16 位数据格式
    this->hspi->Instance->CR1 &= ~SPI_CR1_SPE;
    this->hspi->Instance->CR1 |= SPI_CR1_DFF;
    this->hspi->Instance->CR1 |= SPI_CR1_SPE;

    this->beginWrite();

    for (uint32_t i = 0; i < length; i++) {
        this->hspi->Instance->DR = data[i];
        while ((this->hspi->Instance->SR & SPI_SR_TXE) == 0);
    }

    while ((this->hspi->Instance->SR & SPI_SR_BSY) != 0);

    this->endWrite();

    // 恢复为 8 位数据格式
    this->hspi->Instance->CR1 &= ~SPI_CR1_SPE;
    this->hspi->Instance->CR1 &= ~SPI_CR1_DFF;
    this->hspi->Instance->CR1 |= SPI_CR1_SPE;
}

void SPILcd::setDirection(uint8_t direction) {
    this->Direction = direction;  // 保存方向设置

    this->beginWrite();  // 片选拉低

    switch (direction) {
    case Direction_H: // 横屏
        this->writeCommand(0x36);
        this->writeData(0x70);
        this->X_Offset = 0;
        this->Y_Offset = 0;
        this->Width  = LCD_Width;   
        this->Height = LCD_Height;
        break;

    case Direction_V: // 竖屏
        this->writeCommand(0x36);
        this->writeData(0x00);
        this->X_Offset = 0;
        this->Y_Offset = 0;
        this->Width  = LCD_Width;
        this->Height = LCD_Height;
        break;

    case Direction_H_Flip: // 横屏翻转
        this->writeCommand(0x36);
        this->writeData(0xA0);
        this->X_Offset = 80;
        this->Y_Offset = 0;
        this->Width  = LCD_Width;
        this->Height = LCD_Height;
        break;

    case Direction_V_Flip: // 竖屏翻转
        this->writeCommand(0x36);
        this->writeData(0xC0);
        this->X_Offset = 0;
        this->Y_Offset = 80;
        this->Width  = LCD_Width;
        this->Height = LCD_Height;
        break;

    default:
        break;
    }

    while ((this->hspi->Instance->SR & 0x0080) != 0);  // 等待通信完成
    this->endWrite(); // 片选拉高
}

void SPILcd::setBackColor(uint32_t Color)
{
	uint16_t Red_Value = 0, Green_Value = 0, Blue_Value = 0; //各个颜色通道的值

	Red_Value   = (uint16_t)((Color&0x00F80000)>>8);   // 转换成 16位 的RGB565颜色
	Green_Value = (uint16_t)((Color&0x0000FC00)>>5);
	Blue_Value  = (uint16_t)((Color&0x000000F8)>>3);

	this->BackColor = (uint16_t)(Red_Value | Green_Value | Blue_Value);	// 将颜色写入全局LCD参数			   	
}

void SPILcd::setColor(uint32_t Color)
{
	uint16_t Red_Value = 0, Green_Value = 0, Blue_Value = 0; //各个颜色通道的值

	Red_Value   = (uint16_t)((Color&0x00F80000)>>8);   // 转换成 16位 的RGB565颜色
	Green_Value = (uint16_t)((Color&0x0000FC00)>>5);
	Blue_Value  = (uint16_t)((Color&0x000000F8)>>3);

	this->Color = (uint16_t)(Red_Value | Green_Value | Blue_Value);  // 将颜色写入全局LCD参数		
}

void SPILcd::setAddress(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
    beginWrite();

    // 设置列地址（X 坐标）
    writeCommand(0x2A);
    writeData16(x1 + this->X_Offset);
    writeData16(x2 + this->X_Offset);

    // 设置行地址（Y 坐标）
    writeCommand(0x2B);
    writeData16(y1 + this->Y_Offset);
    writeData16(y2 + this->Y_Offset);

    // 准备写入显存
    writeCommand(0x2C);

    while( (this->hspi->Instance->SR & 0x0080) != RESET);	//	等待通信完成
    endWrite();
}

void SPILcd::clear() {
    /* 寄存器版本 */
    uint32_t totalPixels = this->Width * this->Height;

    // 设置坐标区域
    this->setAddress(0, 0, this->Width - 1, this->Height - 1);

    // 切换 SPI 为 16 位模式
    this->hspi->Instance->CR1 &= ~SPI_CR1_SPE;     // 关闭 SPI
    this->hspi->Instance->CR1 |= SPI_CR1_DFF;      // 16位数据帧
    this->hspi->Instance->CR1 |= SPI_CR1_SPE;      // 重新使能 SPI

    this->beginWrite();

    for (uint32_t i = 0; i < totalPixels; i++) {
        this->hspi->Instance->DR = this->BackColor;
        while ((this->hspi->Instance->SR & SPI_SR_TXE) == 0);  // 等待发送完成
    }

    while ((this->hspi->Instance->SR & SPI_SR_BSY) != 0); // 等待 SPI 总线空闲

    this->endWrite();

    // 恢复 SPI 为 8 位模式
    this->hspi->Instance->CR1 &= ~SPI_CR1_SPE;
    this->hspi->Instance->CR1 &= ~SPI_CR1_DFF;
    this->hspi->Instance->CR1 |= SPI_CR1_SPE;
}


/* ASCII 字符显示 */
void SPILcd::setAsciiFont(pFONT *fonts) {
    this->asciiFont = fonts;
}

void SPILcd::displayChar(uint16_t x, uint16_t y, uint8_t asciiChar) {
    if (asciiFont == nullptr) return;  // 字体未设置直接返回

    uint16_t index = 0, counter = 0, i = 0, w = 0;
    uint8_t disChar;
    uint16_t buffer[512] = {0};  // 缓冲区足够容纳最大 ASCII 字模（如 16x8）

    asciiChar -= 32;  // ASCII 字符偏移（从空格开始）

    for (index = 0; index < asciiFont->Sizes; ++index) {
        disChar = asciiFont->pTable[asciiChar * asciiFont->Sizes + index];
        for (counter = 0; counter < 8; ++counter) {
            if (disChar & 0x01) {
                buffer[i++] = this->Color;
            } else {
                buffer[i++] = this->BackColor;
            }
            disChar >>= 1;
            if (++w == asciiFont->Width) {
                w = 0;
                break;
            }
        }
    }

    this->setAddress(x, y, x + asciiFont->Width - 1, y + asciiFont->Height - 1);
    this->writeBuff(buffer, asciiFont->Width * asciiFont->Height);
}

void SPILcd::displayString(uint16_t x, uint16_t y, char *asciiStr) {
    
	while ((x < this->Width) && (*asciiStr != 0))	//判断显示坐标是否超出显示区域并且字符是否为空字符
	{
        this->displayChar( x,y,*asciiStr);
        x += this->asciiFont->Width; //显示下一个字符
        asciiStr++;	//取下一个字符地址
	}
}


/* 中文及混合文本显示 */
void SPILcd::setTextFont(pFONT *fonts) {
    // TODO: 设置中文字体 + 对应 ASCII 字体
}

void SPILcd::displayChinese(uint16_t x, uint16_t y, char *pText) {
    // TODO: 显示单个中文字符
}

void SPILcd::displayText(uint16_t x, uint16_t y, char *pText) {
    // TODO: 显示包含中文与 ASCII 的混合字符串
}


/* 数字显示 */
void SPILcd::setNumMode(uint8_t mode) {
    this->ShowNum_Mode = mode;
}

void SPILcd::displayNumber(uint16_t x, uint16_t y, int32_t number, uint8_t len) {
	char Number_Buffer[32];				// 用于存储转换后的字符串

	if( this->ShowNum_Mode == Fill_Zero){
		sprintf( Number_Buffer , "%0.*d",len, number );	// 将 number 转换成字符串，便于显示		
	}
	else{	
		sprintf( Number_Buffer , "%*d",len, number );	// 将 number 转换成字符串，便于显示		
	}
	
	displayString( x, y,(char *)Number_Buffer) ;  // 将转换得到的字符串显示出来
}

/*
decs 保留的小数位数
*/
void SPILcd::displayDecimals(uint16_t x, uint16_t y, double decimals, uint8_t len, uint8_t decs) {
	char  Number_Buffer[64];				// 用于存储转换后的字符串
	
	if( this->ShowNum_Mode == Fill_Zero)	// 多余位填充0模式
	{
		sprintf( Number_Buffer , "%0*.*lf",len,decs, decimals );	// 将 number 转换成字符串，便于显示		
	}
	else		// 多余位填充空格
	{
		sprintf( Number_Buffer , "%*.*lf",len,decs, decimals );	// 将 number 转换成字符串，便于显示		
	}
	
	displayString( x, y,(char *)Number_Buffer) ;	// 将转换得到的字符串显示出来
}

void SPILcd::drawPoint565(uint16_t x, uint16_t y, uint16_t color565) {
    this->setAddress(x, y, x, y);  // 设置光标区域
    this->beginWrite();           // 拉低 CS
    this->writeData16(color565);  // 直接写入 RGB565 数据
    while ((this->hspi->Instance->SR & SPI_SR_BSY) != 0);
    this->endWrite();
}

void SPILcd::drawPoint(uint16_t x, uint16_t y, uint32_t color) {
    this->setAddress(x, y, x, y);  // 设置光标区域
    this->beginWrite();  // 拉低 CS
    this->writeData16(static_cast<uint16_t>(color));
    while ((this->hspi->Instance->SR & SPI_SR_BSY) != 0);  // 等待 SPI 空闲
    this->endWrite();  
}

void SPILcd::drawPoint(uint16_t x, uint16_t y) {
    drawPoint(x, y, this->Color);  // 自动使用画笔色
}

void SPILcd::drawLineV(uint16_t x, uint16_t y, uint16_t height) {
    if (height > 1024) height = 1024;  // 防止越界

    for (uint16_t i = 0; i < height; ++i) {
        this->drawBuffer[i] = this->Color;
    }

    this->setAddress(x, y, x, y + height - 1);
    this->writeBuff(this->drawBuffer, height);
}

void SPILcd::drawLineH(uint16_t x, uint16_t y, uint16_t width) {
    if (width > 1024) width = 1024;  // 限制最大长度，避免越界写 buffer

    for (uint16_t i = 0; i < width; ++i) {
        this->drawBuffer[i] = this->Color;
    }

    this->setAddress(x, y, x + width - 1, y);  // 水平线：固定 y，x 范围展开
    this->writeBuff(this->drawBuffer, width);
}

void SPILcd::drawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2) {
    int dx = abs(x2 - x1), sx = x1 < x2 ? 1 : -1;
    int dy = -abs(y2 - y1), sy = y1 < y2 ? 1 : -1;
    int err = dx + dy, e2;

    while (true) {
        this->drawPoint(x1, y1, this->Color);
        if (x1 == x2 && y1 == y2) break;
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; x1 += sx; }
        if (e2 <= dx) { err += dx; y1 += sy; }
    }
}

void SPILcd::drawRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
    this->drawLineH(x, y, width);
    this->drawLineH(x, y + height - 1, width);
    this->drawLineV(x, y, height);
    this->drawLineV(x + width - 1, y, height);
}

void SPILcd::drawCircle(uint16_t x0, uint16_t y0, uint16_t r) {
    int x = -r, y = 0, err = 2 - 2 * r, e2;
    do {
        this->drawPoint(x0 - x, y0 + y, this->Color);
        this->drawPoint(x0 + x, y0 + y, this->Color);
        this->drawPoint(x0 + x, y0 - y, this->Color);
        this->drawPoint(x0 - x, y0 - y, this->Color);
        e2 = err;
        if (e2 <= y) { err += ++y * 2 + 1; if (-x == y && e2 <= x) e2 = 0; }
        if (e2 > x) err += ++x * 2 + 1;
    } while (x <= 0);
}

void SPILcd::drawEllipse(int x0, int y0, int r1, int r2) {
    int x = -r1, y = 0, err = 2 - 2 * r1;
    float k = 0, fr1 = r1, fr2 = r2;
    if (r1 > r2) {
        do {
            k = fr1 / fr2;
            this->drawPoint(x0 - x, y0 + (uint16_t)(y / k), this->Color);
            this->drawPoint(x0 + x, y0 + (uint16_t)(y / k), this->Color);
            this->drawPoint(x0 + x, y0 - (uint16_t)(y / k), this->Color);
            this->drawPoint(x0 - x, y0 - (uint16_t)(y / k), this->Color);
            int e2 = err;
            if (e2 <= y) {
                err += ++y * 2 + 1;
                if (-x == y && e2 <= x) e2 = 0;
            }
            if (e2 > x) err += ++x * 2 + 1;
        } while (x <= 0);
    } else {
        y = -r2; x = 0;
        do {
            k = fr2 / fr1;
            this->drawPoint(x0 - (uint16_t)(x / k), y0 + y, this->Color);
            this->drawPoint(x0 + (uint16_t)(x / k), y0 + y, this->Color);
            this->drawPoint(x0 + (uint16_t)(x / k), y0 - y, this->Color);
            this->drawPoint(x0 - (uint16_t)(x / k), y0 - y, this->Color);
            int e2 = err;
            if (e2 <= x) {
                err += ++x * 3 + 1;
                if (-y == x && e2 <= y) e2 = 0;
            }
            if (e2 > y) err += ++y * 3 + 1;
        } while (y <= 0);
    }
}

void SPILcd::fillRect(uint16_t x, uint16_t y, uint16_t width, uint16_t height) {
    if (width * height > 1024) height = 1024 / width;
    for (uint32_t i = 0; i < width * height; ++i) {
        this->drawBuffer[i] = this->Color;
    }
    this->setAddress(x, y, x + width - 1, y + height - 1);
    this->writeBuff(this->drawBuffer, width * height);
}

void SPILcd::fillCircle(uint16_t x0, uint16_t y0, uint16_t r) {
    int d = 3 - 2 * r;
    int curX = 0, curY = r;
    while (curX <= curY) {
        if (curY > 0) {
            this->drawLineV(x0 - curX, y0 - curY, 2 * curY);
            this->drawLineV(x0 + curX, y0 - curY, 2 * curY);
        }
        if (curX > 0) {
            this->drawLineV(x0 - curY, y0 - curX, 2 * curX);
            this->drawLineV(x0 + curY, y0 - curX, 2 * curX);
        }
        if (d < 0) {
            d += (curX << 2) + 6;
        } else {
            d += ((curX - curY) << 2) + 10;
            --curY;
        }
        ++curX;
    }
    this->drawCircle(x0, y0, r);
}

void SPILcd::fillFlush(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t* color) {
    uint32_t totalPixels = (x2 - x1 + 1) * (y2 - y1 + 1);

    this->setAddress(x1, y1, x2, y2);  // 设置要刷新的区域
    this->beginWrite();               // 片选拉低
    this->writeBuff(color, totalPixels); // 批量写入像素数据
    this->endWrite();                // 片选拉高
}


void SPILcd::drawImage(uint16_t x, uint16_t y, uint16_t width, uint16_t height, const uint8_t *pImage) {
    // TODO: 绘制单色图片
}


void SPILcd::dmaWrite(uint16_t *data, uint32_t length) {
    // beginWrite();
    // dcData();
    // HAL_SPI_Transmit_DMA(hspi, reinterpret_cast<uint8_t *>(data), length * 2);
    // // DMA完成时需在中断中调用 endWrite()
}

void SPILcd::enableBacklight() {
    HAL_GPIO_WritePin(bl_port, bl_pin, GPIO_PIN_SET);
}

void SPILcd::disableBacklight() {
    HAL_GPIO_WritePin(bl_port, bl_pin, GPIO_PIN_RESET);
}
